/*
 * MIT License
 *
 * Copyright (c) 2024-2048 冰羽
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.star;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.WorkbookUtil;
import cn.star.framework.entity.AbstractIdEntity;
import cn.star.framework.gen.model.EntityModel;
import cn.star.framework.gen.model.field.EntityField;
import cn.star.framework.gen.plugin.EntityGenerator;
import com.alibaba.excel.*;
import com.alibaba.excel.annotation.ExcelProperty;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.Data;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.Test;

public class ExcelHandler {

  @Data
  public static class HeaderVo implements Serializable {
    @ExcelProperty(value = {"字段名称（英文）"})
    private String name;

    @ExcelProperty(value = {"字段名称（中文）"})
    private String title;

    @ExcelProperty(value = {"字段类型"})
    private String type;

    @ExcelProperty(value = {"备注"})
    private String comment;

    public boolean isNotEmpty() {
      return StrUtil.isNotEmpty(name);
    }
  }

  @Data
  public static class Mapping {
    private Class<?> type;
    private File excel;

    public Mapping(Class<?> type, File excel) {
      this.type = type;
      this.excel = excel;
    }
  }

  private static final List<Mapping> mappings =
      Arrays.asList(
          new Mapping(HeaderVo.class, new File("/home/zhaoweiping/桌面/zky/1-传播分析系统表结构（硬件与网络运行监控）.xlsx")),
          new Mapping(
              HeaderVo.class, new File("/home/zhaoweiping/桌面/zky/传播分析系统表结构模板（核心应用系统）.xlsx")),
          new Mapping(HeaderVo.class, new File("/home/zhaoweiping/桌面/zky/传播分析系统表结构（系统运行管理）.xlsx")));

  @Test
  public void code() throws Exception {
    for (int i = 0; i < mappings.size(); i++) {
      if (i > 0) {
        break;
      }
      List<EntityModel> entityModels = read(mappings.get(i));
      for (EntityModel model : entityModels) {
        EntityGenerator generator = new EntityGenerator(model);
        generator.gen("/tmp/111111111111111/222");
      }
    }
  }

  private static List<String> getSheetNames(File file) {
    ZipSecureFile.setMinInflateRatio(-1.0);
    Workbook workbook = WorkbookUtil.createBook(file);
    int count = workbook.getNumberOfSheets();
    List<String> list = new ArrayList<>(count);
    for (int i = 0; i < count; i++) {
      list.add(workbook.getSheetName(i));
    }
    return list;
  }

  private static List<EntityModel> read(Mapping mapping) throws Exception {
    List<EntityModel> entityModels = new ArrayList<>();
    List<String> sheetNames = getSheetNames(mapping.getExcel());
    System.out.println(sheetNames);
    for (String sheetName : sheetNames) {
      List<HeaderVo> list =
          EasyExcel.read(mapping.getExcel()).sheet(sheetName).head(mapping.getType()).doReadSync();
      if (list.stream().anyMatch(i -> i.isNotEmpty())) {
        // 获取表名
        String tableName = sheetName, comment = "";
        String[] str = sheetName.split("-");
        if (str.length > 1) {
          comment = str[0];
          tableName = str[1];
        }
        String entityName = StrUtil.toCamelCase(tableName);
        entityName = entityName.substring(0, 1).toUpperCase() + entityName.substring(1);

        EntityModel model = generator(entityName, tableName, comment, list);
        entityModels.add(model);
      }
    }

    return entityModels;
  }

  private static EntityModel generator(
      String entityName, String tableName, String comment, List<HeaderVo> list) {
    EntityModel model = new EntityModel(entityName).table(tableName).comment(comment);
    model.setBasePackage("cn.star");
    model.setSuperEntityClass(AbstractIdEntity.class);

    model.setFields(
        list.stream()
            .map(
                i -> {
                  String name = i.getName();
                  if (name.indexOf('_') > 0) {
                    name = name.toLowerCase();
                  }
                  if ("id".equalsIgnoreCase(name)) {
                    return null;
                  }
                  name = StrUtil.toCamelCase(name);
                  EntityField field = new EntityField(name);
                  field
                      .getColumn()
                      .setComment(StrUtil.isBlank(i.getComment()) ? i.getTitle() : i.getComment());
                  field.setDescribe(i.getTitle());
                  String type = StrUtil.format("{}", i.getType()).toLowerCase();
                  if (type.contains("date")) {
                    field.setType(Date.class);
                  } else if (type.contains("int")) {
                    field.getColumn().setLength(11);
                    field.setType(Integer.class);
                  } else if (type.contains("long")) {
                    field.getColumn().setLength(19);
                    field.setType(Long.class);
                  } else if (type.contains("lob")) {
                    field.setType(String.class);
                    field.setLob(true);
                  } else if (type.contains("char(1)")) {
                    field.getColumn().setLength(1);
                    field.setType(String.class);
                  } else if (type.contains("varchar(40")) {
                    field.getColumn().setLength(40);
                    field.setType(String.class);
                  } else if (type.contains("varchar(20")) {
                    field.getColumn().setLength(20);
                    field.setType(String.class);
                  } else if (type.contains("varchar(32")) {
                    field.getColumn().setLength(32);
                    field.setType(String.class);
                  } else if (type.contains("varchar(64")) {
                    field.getColumn().setLength(64);
                    field.setType(String.class);
                  } else if (type.contains("varchar(400")) {
                    field.getColumn().setLength(400);
                    field.setType(String.class);
                  } else if (type.contains("varchar(1024")) {
                    field.getColumn().setLength(1024);
                    field.setType(String.class);
                  } else {
                    field.getColumn().setLength(111);
                    field.setType(String.class);
                  }
                  return field;
                })
            .filter(Objects::nonNull)
            .collect(Collectors.toList()));

    return model;
  }

  @Test
  public void genTypescriptCodesForServiceTsx() throws Exception {
    for (int i = 0; i < mappings.size(); i++) {
      if (i > 0) {
        break;
      }
      List<EntityModel> entityModels = read(mappings.get(i));
      StringBuffer sb = new StringBuffer("import { request } from '@/utils/util';\n\n");
      for (EntityModel model : entityModels) {
        String code = genServiceTsx(model);
        sb.append(code);
      }
      System.out.println(sb.toString());
    }
  }

  private static String genServiceTsx(EntityModel model) {
    EntityGenerator generator = new EntityGenerator(model);
    String rest = generator.getControllerModel().getRest().replace("/", "");
    String name = model.getName();
    StringBuffer sb = new StringBuffer();
    sb.append((StrUtil.format("export const save{} = async (params?: any) => {\n", name)));
    sb.append(StrUtil.format("  return request(`/{}`, {\n", rest));
    sb.append("    method: 'post',\n");
    sb.append("    data: params,\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    sb.append(StrUtil.format("export const update{} = async (params?: any) => {\n", name));
    sb.append(StrUtil.format("  return request(`/{}`, {\n", rest));
    sb.append("    method: 'put',\n");
    sb.append("    data: params,\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    sb.append(StrUtil.format("export const deleteBy{}Ids = async (params?: any) => {\n", name));
    sb.append(StrUtil.format("  return request(`/{}`, {\n", rest));
    sb.append("    method: 'delete',\n");
    sb.append("    data: params,\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    sb.append(StrUtil.format("export const list{} = async (params?: any) => {\n", name));
    sb.append(StrUtil.format("  return request(`/{}`, {\n", rest));
    sb.append("    method: 'get',\n");
    sb.append("    params,\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    sb.append(StrUtil.format("export const get{} = async (params?: any) => {\n", name));
    sb.append(StrUtil.format("  return request(`/{}/${params?.id}`, {\n", rest));
    sb.append("    method: 'get',\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    sb.append(StrUtil.format("export const page{} = async (params?: any) => {\n", name));
    sb.append(StrUtil.format("  return request(`/{}/page`, {\n", rest));
    sb.append("    method: 'get',\n");
    sb.append("    params,\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    sb.append(StrUtil.format("export const imp{} = async (params?: any) => {\n", name));
    sb.append(StrUtil.format("  return request(`/{}/imp`, {\n", rest));
    sb.append("    method: 'post',\n");
    sb.append("    data: params,\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    sb.append(StrUtil.format("export const exp{} = async (params?: any) => {\n", name));
    sb.append(StrUtil.format("  return request(`/{}/exp`, {\n", rest));
    sb.append("    method: 'get',\n");
    sb.append("    params,\n");
    sb.append("  });\n");
    sb.append("};\n\n");

    return sb.toString();
  }

  @Test
  public void genTypescriptCodesForPageableTsx() throws Exception {
    for (int i = 0; i < mappings.size(); i++) {
      if (i > 0) {
        break;
      }
      String dir = "/home/zhaoweiping/workspace/zky/cas-ui/src/pages/cas";
      List<EntityModel> entityModels = read(mappings.get(i));
      for (EntityModel model : entityModels) {
        PageableTsxCode code = genPageableTsx(model);
        String file = StrUtil.format("{}/{}", dir, code.getName());
        FileUtil.writeUtf8String(code.getCode(), file);
      }
    }
  }

  @Data
  public static class PageableTsxCode {
    private String name;
    private String code;

    public PageableTsxCode(String name, String code) {
      this.name = name;
      this.code = code;
    }
  }

  private static PageableTsxCode genPageableTsx(EntityModel model) {
    EntityGenerator generator = new EntityGenerator(model);
    String file = generator.getControllerModel().getRest().replace("/", "") + "/index.tsx";
    String name = model.getName();
    StringBuffer sb = new StringBuffer();
    sb.append("import React, { useRef } from 'react';\n");
    sb.append("import { ColumnType, SuperTable, SuperTableActionType, SuperTableProps } from '@/components/SuperTable';\n");
    sb.append(StrUtil.format("import { save{}, update{}, deleteBy{}Ids, page{} } from '@/pages/cas/service';\n", name, name, name, name));
    sb.append("import { factory } from '@/utils/column';\n");
    sb.append("import { responseAfterHandler } from '@/utils/util';\n");
    sb.append("import { message } from 'antd';\n");
    sb.append("\n");
    sb.append("export default () => {\n");
    sb.append("  const ref = useRef<SuperTableActionType<any>>();\n");
    sb.append("  const columns: SuperTableProps<any>['columns'] = factory.wrapperProColumns([\n");
    sb.append("    ...factory.getAbstractIdBaseEntityColumns() as any,\n");
    for (EntityField field : model.getFields()) {
      sb.append(StrUtil.format("    { dataIndex: '{}', title: '{}', },\n", field.getName(), field.getDescribe()));
    }
    sb.append("  ]);\n");
    sb.append("\n");
    sb.append("return (\n");
    sb.append("    <SuperTable<any>\n");
    sb.append(StrUtil.format("      instance={ref} columns={columns} fetch={page{}} \n", name));
    sb.append(StrUtil.format("      create={{submit: async (data) => responseAfterHandler(await save{}(data), () => {message.success('新增成功');ref.current?.reload();})}}\n", name));
    sb.append(StrUtil.format("      update={{submit: async (data) => responseAfterHandler(await update{}({...ref.current?.getSelectedRows()[0], ...data}), () => {message.success('更新成功');ref.current?.reload();}), fetch: async () => Promise.resolve({code: '200', data: ref.current?.getSelectedRows()[0]})}}\n", name));
    sb.append(StrUtil.format("      delete={{submit: async (data) => responseAfterHandler(await deleteBy{}Ids(data), () => {message.success('删除成功');ref.current?.reload();})}}\n", name));
    sb.append("      toolBarRenderMerge={() => {\n");
    sb.append("        return { import: { hidden: true }, export: { hidden: true }, clone: { hidden: true }, }\n");
    sb.append("      }}\n");
    sb.append("    />\n");
    sb.append("  );\n");
    sb.append("};\n");
    sb.append("\n");

    return new PageableTsxCode(file, sb.toString());
  }

  @Test
  public void genTypescriptCodesForRouteTsx() throws Exception {
    for (int i = 0; i < mappings.size(); i++) {
      if (i > 0) {
        break;
      }
      List<EntityModel> entityModels = read(mappings.get(i));
      for (EntityModel model : entityModels) {
        genRouteTsx(model);
      }
    }
  }

  private static void genRouteTsx(EntityModel model) {
    EntityGenerator generator = new EntityGenerator(model);
    String rest = generator.getControllerModel().getRest().replace("/", "");
    StringBuffer sb = new StringBuffer();
    sb.append(StrUtil.format("{path: '{}', component: '@/pages/cas/{}/index'},", rest, rest));
    System.out.println(sb.toString());
  }

  @Test
  public void genTypescriptCodesForMenuTsx() throws Exception {
    for (int i = 0; i < mappings.size(); i++) {
      if (i > 0) {
        break;
      }
      List<EntityModel> entityModels = read(mappings.get(i));
      for (EntityModel model : entityModels) {
        genMenuTsx(model);
      }
    }
  }

  private static void genMenuTsx(EntityModel model) {
    EntityGenerator generator = new EntityGenerator(model);
    String rest = generator.getControllerModel().getRest().replace("/", "");
    StringBuffer sb = new StringBuffer();
    sb.append(StrUtil.format("{path: '/cas/{}', name: '{}', icon: <FundOutlined />},", rest, model.getComment()));
    System.out.println(sb.toString());
  }
}
